package se.jocke.nbrs.service.impl;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileUtil;
import org.openide.modules.Places;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.lookup.ServiceProvider;
import se.jocke.nbrs.model.RestService;
import se.jocke.nbrs.model.RestServiceGroup;
import se.jocke.nbrs.model.RestServiceGroups;
import se.jocke.nbrs.service.EventBus;
import se.jocke.nbrs.service.RestServiceManager;
import se.jocke.nbrs.service.event.RestServiceGroupChanged;

@ServiceProvider(service = RestServiceManager.class)
public class RestServiceManagerImpl implements RestServiceManager {

    private static final Map<String, RestServiceGroup> GROUPS = new HashMap<String, RestServiceGroup>();

    private static final String REST_DATA_XML = "rest-services.xml";

    private static final AtomicBoolean HAS_FILE_LISTENER = new AtomicBoolean(false);

    private static final String INIT_CONTENT
            = "<object class=\"se.jocke.nbrs.model.RestServiceGroups\">\n \n </object>";

    public RestServiceManagerImpl() {
    }

    @Override
    public List<RestServiceGroup> getGroups() {

        if (GROUPS.isEmpty()) {
            loadGroups();
        }

        return new ArrayList<RestServiceGroup>(GROUPS.values());
    }

    private void loadGroups() {

        try {
            File doc = new File(Places.getUserDirectory(), REST_DATA_XML);

            if (doc.exists()) {
                XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream(doc)));
                RestServiceGroups groups = (RestServiceGroups) decoder.readObject();
                decoder.close();

                if (groups.getGroups() != null) {
                    for (RestServiceGroup restServiceGroup : groups.getGroups()) {
                        GROUPS.put(restServiceGroup.getName(), restServiceGroup);
                    }
                }
            } else {
                try {
                    doc.createNewFile();
                    FileUtil.copy(new ByteArrayInputStream(
                            INIT_CONTENT.getBytes()), new FileOutputStream(doc));
                } catch (IOException ex) {
                    Logger.getLogger(RestServiceManagerImpl.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            if (HAS_FILE_LISTENER.compareAndSet(false, true)) {
                org.openide.filesystems.FileObject data = FileUtil.createData(doc);
                data.addFileChangeListener(new FileChangeAdapter() {
                    @Override
                    public void fileChanged(FileEvent fe) {
                        loadGroups();
                        fireChangeEvent();
                        super.fileChanged(fe);
                    }
                });
            }

        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }

    }

    @Override
    @SuppressWarnings("unchecked")
    public void saveRestService(RestServiceGroup group, RestService service) {
        group.update(service);
        GROUPS.put(group.getName(), group);
        fireChangeEvent();
        persistChanges();

    }

    private void fireChangeEvent() {
        Lookup.getDefault().lookup(EventBus.class).post(new RestServiceGroupChanged());
    }

    private void persistChanges() {
        try {
            File userDirectory = Places.getUserDirectory();

            File doc = new File(userDirectory, REST_DATA_XML);

            if (!doc.exists()) {
                try {
                    doc.createNewFile();
                } catch (IOException ex) {
                    Logger.getLogger(RestServiceManagerImpl.class.getName()).log(Level.SEVERE, null, ex);
                    return;
                }
            }

            Logger.getLogger(RestServiceManagerImpl.class.getName()).log(Level.INFO, "Saving to file {0}", doc.getPath());

            XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(doc)));
            encoder.writeObject(new RestServiceGroups(new ArrayList(GROUPS.values())));
            encoder.flush();
            encoder.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(RestServiceManagerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }

        loadGroups();
    }

    @Override
    public void addNewGroup(String serviceGroupName) {
        GROUPS.put(serviceGroupName, new RestServiceGroup(serviceGroupName));
        persistChanges();
        loadGroups();
        fireChangeEvent();
    }

    @Override
    public void addNewService(String serviceGroupName, String name) {
        if (GROUPS.containsKey(serviceGroupName)) {
            GROUPS.get(serviceGroupName).add(new RestService(name));
            persistChanges();
            loadGroups();
            fireChangeEvent();
        }
    }
}
